import os
import PySpin
import sys
from PyQt5.QtCore import QElapsedTimer

NUM_IMAGES = 100
class EventType:
    """
    'Enum' for choosing whether to register a event specifically for exposure end events
    or universally for all events.
    """
    GENERIC = 0
    SPECIFIC = 1

CHOSEN_EVENT = EventType.GENERIC

class DeviceEventHandler(PySpin.DeviceEventHandler):
    def __init__(self, eventname):
        print('*', eventname)
        super(DeviceEventHandler, self).__init__()
        self.event_name = eventname
        self.count = 0

    def OnDeviceEvent(self, eventname):
        pass
        # print('**',eventname)
        # if eventname == self.event_name:
        #     self.count += 1
        #     print('\tDevice event %s with ID %i number %i...' % (eventname,
        #                                                        self.GetDeviceEventId(),
        #                                                        self.count))
        # else:
        #     print('\tDevice event occurred; not %s; ignoring...' % self.event_name)

def configure_device_events(nodemap, cam):
    print('\n*** CONFIGURING DEVICE EVENTS ***\n')

    try:
        result = True

        #检索设备事件选择器
        node_event_selector = PySpin.CEnumerationPtr(nodemap.GetNode('EventSelector'))
        if not PySpin.IsAvailable(node_event_selector) or not PySpin.IsReadable(node_event_selector):
            print('Unable to retrieve event selector entries. Aborting...')
            return False

        entries = node_event_selector.GetEntries()
        print('Enabling event selector entries...')

        # 激活事件声明
        for entry in entries:
            node_entry = PySpin.CEnumEntryPtr(entry)
            if not PySpin.IsAvailable(node_entry) or not PySpin.IsReadable(node_entry):
                result = False
                continue

            node_event_selector.SetIntValue(node_entry.GetValue())

            # 检索事件声明
            node_event_notification = PySpin.CEnumerationPtr(nodemap.GetNode('EventNotification'))
            if not PySpin.IsAvailable(node_event_notification) or not PySpin.IsWritable(node_event_notification):
                result = False
                continue

            node_event_notification_on = PySpin.CEnumEntryPtr(node_event_notification.GetEntryByName('On'))
            if not PySpin.IsAvailable(node_event_notification_on) or not PySpin.IsReadable(node_event_notification_on):
                result = False
                continue
            #开始时间声明
            node_event_notification.SetIntValue(node_event_notification_on.GetValue())

            print('\t%s: enabled...' % node_entry.GetDisplayName()) #事件选择器三个选项：AcquisitionStart、AcquisitionEnd、ExposureEnd

        device_event_handler = DeviceEventHandler('EventExposureEnd') #实例化设备事件

        # 注册设备事件，与相机连接
        #记得之后要注销事件
        if CHOSEN_EVENT == EventType.GENERIC:

            # Device event handlers registered generally will be triggered by any device events.
            cam.RegisterEventHandler(device_event_handler)

            print('Device event handler registered generally...')

        elif CHOSEN_EVENT == EventType.SPECIFIC:
            cam.RegisterEventHandler(device_event_handler, 'EventExposureEnd')

            print('Device event handler registered specifically to EventExposureEnd events...')

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        result = False

    return result, device_event_handler

def reset_device_events(cam, device_event_handler):
    try:
        result = True
        cam.UnregisterEventHandler(device_event_handler)
        print('Device event handler unregistered...\n')

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        result = False

    return result

def acquire_images(cam, nodemap, nodemap_tldevice):
    print('\n*** IMAGE ACQUISITION ***\n')
    try:
        result = True
        #设置接收模式为连续
        node_acquisition_mode = PySpin.CEnumerationPtr(nodemap.GetNode('AcquisitionMode'))
        if not PySpin.IsAvailable(node_acquisition_mode) or not PySpin.IsWritable(node_acquisition_mode):
            print('Unable to set acquisition mode to continuous (enum retrieval). Aborting...\n')
            return False

        node_acquisition_mode_continuous = node_acquisition_mode.GetEntryByName('Continuous')
        if not PySpin.IsAvailable(node_acquisition_mode_continuous) \
                or not PySpin.IsReadable(node_acquisition_mode_continuous):
            print('Unable to set acquisition mode to continuous (entry retrieval). Aborting...\n')
            return False

        acquisition_mode_continuous = node_acquisition_mode_continuous.GetValue()
        node_acquisition_mode.SetIntValue(acquisition_mode_continuous)
        print('Acquisition mode set to continuous...')

        cam.BeginAcquisition()

        print('Acquiring images...')

        node_device_serial_number = PySpin.CStringPtr(nodemap_tldevice.GetNode('DeviceSerialNumber'))
        if PySpin.IsAvailable(node_device_serial_number) and PySpin.IsReadable(node_device_serial_number):
            device_serial_number = node_device_serial_number.GetValue()
            print('Device serial number retrieved as %s...' % device_serial_number)
        elapsedTime = QElapsedTimer()
        elapsedTime.start()

        for i in range(NUM_IMAGES):
            try:
                print('***')
                image_result = cam.GetNextImage(1000)  # 这个触发了设备的响应
                print('***')
                if image_result.IsIncomplete():
                    print('Image incomplete with image status %s...' % image_result.GetImageStatus())
                else:
                    image_converted = image_result.Convert(PySpin.PixelFormat_Mono8, PySpin.HQ_LINEAR)
                    image_array = image_converted.GetNDArray()
                image_result.Release()

            except PySpin.SpinnakerException as ex:
                print('Error: %s' % ex)
                result = False
        time = elapsedTime.elapsed()
        print(NUM_IMAGES/(time*0.001))
        cam.EndAcquisition()

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        result = False

    return result

def configure_exposure(cam):
    """
     This function configures a custom exposure time. Automatic exposure is turned
     off in order to allow for the customization, and then the custom setting is
     applied.

     :param cam: Camera to configure exposure for.
     :type cam: CameraPtr
     :return: True if successful, False otherwise.
     :rtype: bool
    """

    print('*** CONFIGURING EXPOSURE ***\n')

    try:
        result = True

        # 关闭自动曝光
        if cam.ExposureAuto.GetAccessMode() != PySpin.RW:
            print('Unable to disable automatic exposure. Aborting...')
            return False

        cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Off)
        print('Automatic exposure disabled...')

        # Set exposure time manually; exposure time recorded in microseconds
        #手动设置曝光时间，曝光时间为微秒级别

        if cam.ExposureTime.GetAccessMode() != PySpin.RW:
            print('Unable to set exposure time. Aborting...')
            return False

        # Ensure desired exposure time does not exceed the maximum
        exposure_time_to_set = cam.ExposureTime.GetMin()
        # exposure_time_to_set = min(cam.ExposureTime.GetMax(), exposure_time_to_set)
        cam.ExposureTime.SetValue(exposure_time_to_set)
        print('Shutter time set to %s us...\n' % exposure_time_to_set)

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex)
        result = False

    return result

def run_single_camera(cam):
    try:
        result = True
        nodemap_tldevice = cam.GetTLDeviceNodeMap()
        # result &= print_device_info(nodemap_tldevice)
        cam.Init()
        nodemap = cam.GetNodeMap()
        configure_exposure(cam)
        err, device_event_handler = configure_device_events(nodemap, cam)
        if not err:
            return err
        result &= acquire_images(cam, nodemap, nodemap_tldevice)
        result &= reset_device_events(cam, device_event_handler)
        cam.DeInit()

    except PySpin.SpinnakerException as ex:
        print('Error: %s' % ex.message)
        result = False

    return result

def main():
    try:
        test_file = open('test.txt', 'w+')
    except IOError:
        print('Unable to write to current directory. Please check permissions.')
        input('Press Enter to exit...')
        return False

    test_file.close()
    os.remove(test_file.name)

    result = True
    system = PySpin.System.GetInstance()
    version = system.GetLibraryVersion()
    print('Library version: %d.%d.%d.%d' % (version.major, version.minor, version.type, version.build))
    cam_list = system.GetCameras()
    num_cameras = cam_list.GetSize()
    print('Number of cameras detected: %d' % num_cameras)

    if num_cameras == 0:
        cam_list.Clear()
        system.ReleaseInstance()

        print('Not enough cameras!')
        input('Done! Press Enter to exit...')
        return False
    for i, cam in enumerate(cam_list):

        print('Running example for camera %d...' % i)

        result &= run_single_camera(cam)
        print('Camera %d example complete... \n' % i)

    del cam
    cam_list.Clear()
    system.ReleaseInstance()
    input('Done! Press Enter to exit...')
    return result

if __name__ == '__main__':
    if main():
        sys.exit(0)
    else:
        sys.exit(1)